import java.net.URL;
import java.util.ArrayList;
 
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
 
import org.w3c.dom.CharacterData;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXParseException;

import java.io.*;

 
public class RSSReader {
 
	private static RSSReader instance = null;
	private int debug =0;
	
	private int count;
	
	private RSSReader() {
		count=0;
	}
	
	public static RSSReader getInstance() {
		if(instance == null) {
			instance = new RSSReader();	
		}
		return instance;
	}
	
	/**
	 * Opens a RSS feed and return a nodelist dependent on what the root node of an article is
	 * 
	 * For REUTERS - entry
	 * For Guardian - item
	 * 
	 * @param link
	 * @param node
	 * @return
	 * @throws Exception
	 */
	private NodeList openFeed(String link,String node) throws Exception
	{
		DocumentBuilder builder = DocumentBuilderFactory.newInstance().newDocumentBuilder();
		URL u = new URL(link);
		InputStream is = u.openStream();
		Document doc = builder.parse(is);
		
		return doc.getElementsByTagName(node);
	}
	
	public article getArticle(File file)
	{
		article art = new article();
		count=0;
		
		try{
			//Parse inputstream from file
			InputStream is = new FileInputStream(file);
			DocumentBuilder builder = DocumentBuilderFactory.newInstance().newDocumentBuilder();
			
			Document doc = builder.parse(is);
			
			
			//Only one node in nodelist
			NodeList nodes = doc.getElementsByTagName("item");
			Element element = (Element)nodes.item(0);
			
			
			
			//Add both title and text to string
			art.setTitle(getElementValue(element,"title"));
			art.setText(getElementValue(element,"text"));
			art.category = Integer.parseInt(getElementValue(element,"category"));
			art.setSource(getElementValue(element,"text"));
			
			is.close();
		} catch (SAXParseException sax)
		{
			if(count>1) System.exit(0);
			
			count++;
			System.err.println("SAX ERROR: "+sax);
		} catch (Exception e)
		{
			System.err.println("Error when reading training text file: "+ e);
		}
		
		return art;
	}
	
	public String trainTextRead(File file)
	{
		String text ="";
		count=0;
		
		try{
			//Parse inputstream from file
			InputStream is = new FileInputStream(file);
			DocumentBuilder builder = DocumentBuilderFactory.newInstance().newDocumentBuilder();
			
			Document doc = builder.parse(is);
			
			
			//Only one node in nodelist
			NodeList nodes = doc.getElementsByTagName("item");
			Element element = (Element)nodes.item(0);
			
			
			
			//Add both title and text to string
			text = getElementValue(element,"title");
			text = text + " " + getElementValue(element,"text");
			
			if(debug==1) System.out.println("TEXT FROM FILE: "+text);
			
			is.close();
		} catch (SAXParseException sax)
		{
			if(count>1) System.exit(0);
			
			count++;
			System.err.println("SAX ERROR: "+sax);
			text = trainTextRead(cleanString(TFile.toString(file)));
		} catch (Exception e)
		{
			System.err.println("Error when reading training text file: "+ e);
		}
		
		return text;
	}
	
	public String trainTextRead(String s)
	{
		String text ="";
		
		try{
			//Parse inputstream from file
			InputStream is = new ByteArrayInputStream(s.getBytes());
			DocumentBuilder builder = DocumentBuilderFactory.newInstance().newDocumentBuilder();
			
			Document doc = builder.parse(is);
			
			
			//Only one node in nodelist
			NodeList nodes = doc.getElementsByTagName("item");
			Element element = (Element)nodes.item(0);
			
			
			
			//Add both title and text to string
			text = getElementValue(element,"title");
			text = text + " " + getElementValue(element,"text");
			
			if(debug==1) System.out.println("TEXT FROM FILE: "+text);
			
			is.close();
		} catch (SAXParseException sax)
		{
			if(count>1) 
				{
					System.err.println("COULD NOT PARSE: "+s);
					return "-";
				}
			count++;
			System.err.println("SAX ERROR: "+sax);
			text = trainTextRead(cleanString(s));
		} catch (Exception e)
		{
			System.err.println("Error when reading training text file: "+ e);
		}
		
		count =0;
		
		return text;
	}
	
	/**
	 * Removes all characters that could cause errors with xml parsing
	 * @param s
	 * @return
	 */
	private String cleanString(String s)
	{
		char[] data = s.toCharArray();
		char c;
		
		for(int i=0;i<data.length;i++)
		{
			c = data[i];
			
			if((!Character.isLetterOrDigit(c) && !validChar(c)) || (c>123)) data[i] = 32;
		}
		
		System.out.println(new String(data));
		return new String(data);
	}
	
	private boolean validChar(char c)
	{
		if(c=='<') return true;
		else if(c=='>') return true;
		else if(c=='/') return true;
		else if(c=='-') return true;
		else if(c=='.') return true;
		else if(c==',') return true;
		else if(c=='?') return true;
		else if(c=='!') return true;
		
		return false;
	}
	
	/**
	 * Reads a given Guardian RSS feed
	 */
	public ArrayList<article> guardianFeed(String link, String topic) {
		int count = 0;
		ArrayList<article> arts = new ArrayList<article>();
		
		String text;
		String title;
		
		try {
			NodeList nodes = openFeed(link,"item");
			pageParser p = new pageParser();
			
			for(int i=0;i<nodes.getLength();i++) {
				
				Element element = (Element)nodes.item(i);
				
				text = p.getGuardian(getElementValue(element,"description"));
				link = getElementValue(element,"link");
				title = getElementValue(element,"title");
				
				if(debug==1)
				{	
					System.out.println("Title: " + title);
					System.out.println("Link: " + link);
					System.out.println("Description formatted: " + text);
					System.out.println();
				}
				
				//Add article to arts
				arts.add(new article(title,link,"Guardian UK",text,topic));
				
				count++;
			}		
		}
		
		catch(Exception ex) {
		ex.printStackTrace();	
		}
		if(debug==1) System.out.println("Count is "+ count);
	
		return arts;
	}

	/**
	 * Reads a given REUTERS RSS feed
	 */
	public ArrayList<article> reutersFeed(String link, String topic) {
		int count = 0;
		ArrayList<article> arts = new ArrayList<article>();
		
		String text;
		String title;
		
		try {
			NodeList nodes = openFeed(link,"entry");
			pageParser p = new pageParser();
			
			for(int i=0;i<nodes.getLength();i++) {
				
				Element element = (Element)nodes.item(i);
				
				text = p.getGuardian(getElementValue(element,"content"));
				link = getElementValue(element,"id");
				title = getElementValue(element,"title");
				
				if(debug==1)
				{	
					System.out.println("Title: " + title);
					System.out.println("Link: " + link);
					System.out.println("Description formatted: " + text);
					System.out.println();
				}
				
				//Add article to arts
				arts.add(new article(title,link,"REUTERS",text,topic));
				
				count++;
			}		
		}
		
		catch(Exception ex) {
		ex.printStackTrace();	
		}
		if(debug==1) System.out.println("Count is "+ count);
	
		return arts;
	}
	
		
	private String getCharacterDataFromElement(Element e) {
		try {
				Node child = e.getFirstChild();
				if(child instanceof CharacterData) {
					CharacterData cd = (CharacterData) child;
					return cd.getData();
				}
			}
			catch(Exception ex) {
				
			}
			return "";			
		} //private String getCharacterDataFromElement
		
		protected float getFloat(String value) {
			if(value != null && !value.equals("")) {
				return Float.parseFloat(value);	
			}
			return 0;
		}
		
		protected String getElementValue(Element parent,String label) {
			return getCharacterDataFromElement((Element)parent.getElementsByTagName(label).item(0));	
		}
		
		public static void main(String[] args) {
		RSSReader reader = RSSReader.getInstance();
		//reader.guardianFeed("http://www.guardian.co.uk/stage","stage");
		//reader.reutersFeed("http://spotlight.reuters.com/api/feed/us/channelarticles/newsOne/atom?apikey=45c8b18f32e0eabf0367dc3135713732&version=1.0", "World");
		//reader.trainTextRead(new File("training/stage/text/stage30.txt"));
		reader.getArticle(new File("training/test/unsorted/durham-unknown-146"));
		}
}
